import React from 'react'
import { createStore, createLogger } from 'relite'
import createViewWrapper from './createViewWrapper'
import BaseView from '../components/BaseView'
import ReactDOM from 'react-dom'
import * as sharedActions from './actions'
import * as $helpers from '../helpers'

export default class BaseController {
  constructor(location, context) {
    this.handlers = {}
    this.context = context
    this.fetch = this.fetch.bind(this)
  }
  async init() {
    const {
      context,
      actions,
      initialState,
      location,
      handlers,
      preload
    } = this
    this.combineHandlers(this)
    let promiseList = []
    const finalActions = { ...sharedActions, ...actions }
    let finalInitialState = { ...initialState }
    if (this.getInitialState) {
      finalInitialState = await this.getInitialState(finalInitialState)
    }
    const store = this.store = createStore(finalActions, finalInitialState)
    if (preload) {
      promiseList.push(this.fetchPreload())
    }
    if (this.shouldComponentCreate) {
      const result = await this.shouldComponentCreate()
      if (result === false) {
        return null
      }
    }
    if (this.componentWillCreate) {
      promiseList.push(this.componentWillCreate())
    }
    if (promiseList.length) {
      await Promise.all(promiseList)
    }
    if (context.isClient) {
      this.logger = createLogger({
        name: this.name || location.pattern
      })
      let unsubscribeList = []
      const unsubscribe = store.subscribe(this.subscriber.bind(this))
      unsubscribeList.push(unsubscribe)

      this.unsubscribeList = unsubscribeList
    }
    this.ViewWrapper = createViewWrapper(this)
    return this.render()
  }
  render() {
    const {
      View,
      store,
      handlers,
      ViewWrapper,
      context
    } = this
    const state = store.getState()
    const componentContext = {
      state,
      handlers,
      actions: store.actions,
      preload: context.preload || {}
    }
    return (
      <BaseView context={componentContext}>
        <ViewWrapper state={state} handlers={handlers} />
      </BaseView>
    )
  }
  subscriber(data) {
    const { context, logger } = this
    if (context.isServer) {
      return
    }
    logger(data)
    // this.refreshView()
    console.time('React#render')
    ReactDOM.render(this.render(), document.getElementById('root'))
    setTimeout(() => {
      console.timeEnd('React#render')
    })
  }
  // 绑定 handler 的 this 值为 controller 实例
  combineHandlers(source) {
    const { handlers } = this
    Object.keys(source).forEach(key => {
      let value = source[key]
      if (key.indexOf('handle') === 0 && 'function' === typeof value) {
        handlers[key] = value.bind(this)
      }
    })
  }
  fetch(url, options = {}) {
    const { context } = this
    // 对 mock 的请求进行另一种拼接，转到 node.js 服务去
    if (url.indexOf('/mock/') === 0) {
      url = context.basename + url
    }
    let fetchData = fetch(url, options)
    if (!options.raw) {
      // fetch data return json format
      fetchData = fetchData.then(response => response.json())
    }
    let promiseList = [fetchData]
    promiseList.push(fetchData)
    if ('number' === typeof options.timeout) {
      const timeoutReject = new Promise((resolve, reject) => {
        setTimeout(
          () => reject(`Fetch Timeout: ${options.timeout}ms`),
          options.timeout
        )
      })
      promiseList.push(timeoutReject)
    }
    return Promise.race(promiseList)
  }
  fetchPreload() {
    const { preload, context } = this
    const keys = Object.keys(preload)
    if (keys.length === 0) {
      return void 0
    }
    const list = keys.map(name => {
      const url = preload[name]
      return fetch(context.basename + '/static' + url)
        .then(response => response.text())
        .then(content => {
          context.preload[name] = content
        })
    })
    return Promise.all(list)
  }
  redirect(url, options = {}) {
    const { context } = this
    if ($helpers.isAbsoluteUrl(url)) {
      if (context.isServer) {
        context.res.redirect(url)
        return
      }
      return location.href = url
    }
    if (context.isClient) {
      let finalUrl = context.basename + url
      if (options.isRaw) {
        finalUrl = location.origin + url
      }
      location.href = finalUrl
    } else {
      let finalUrl = context.basename + url
      if (options.isRaw) {
        finalUrl = url
      }
      context.res.redirect(finalUrl)
    }
  }
}
